package holdem.Rulegui;
import holdem.ListInt;

import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Scanner;
import java.util.Vector;


public class Expression implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1297493653296861263L;
	private Expression left;
	private Expression right;
	private String Operand;
	private Code leaf;
	public  static final String[] operands = { "/\\" , "\\/" , "Y", "!=", "==" , ">=", "<=", "<", ">", "Min", "Max", "+", "-", "*", "/"}; 
	//priotiy of operands from lostest to highest
	//( ) are supported as the highest
	
	
	public void setLeaf(Code c){
		this.leaf = c;
	}
	public void setRight(Expression express){
		right = express;
	}
	public void setLeft(Expression express){
		left = express;
	}
	public void setOperand(String operand){
		Operand = operand;
	}
	
	public boolean isLeaf(){
		return leaf != null;
	}
	public boolean isRight(){
		return right != null;
	}
	public boolean isLeft(){
		return left != null;
	}
	public boolean isTree(){
		return (left != null || right != null) && Operand != null ;
	}
	public Expression getLeft(){
		return left;
	}
	public Expression getRight(){
		return right;
	}
	public String getOperand(){
		return Operand;
	}
	public Code getLeaf(){
		return leaf;
	}
	public String saveString(){
		if(isLeaf()){
			return getLeaf().saveString();
		}
		String right= "";
		String left = "";
		if(isLeft()){
			if(higherPri(this, getLeft())){
				left = getLeft().saveString();
			}
			else{
				left = "(" + getLeft().saveString() + ")";
			}
		}
		if(isRight()){
			if(higherPri(this, getRight())){
				right = getRight().saveString();
			}
			else{
				right = "(" + getRight().saveString() + ")";
			}
		}
		return left + Operand + right;
	}
	private boolean higherPri(Expression high , Expression low){
		int PriHigh;
		for(PriHigh = 0 ; PriHigh < operands.length;PriHigh++){
			if(operands[PriHigh].equals(high.getOperand())){
				break;
			}
		}
		int PriLow;
		for(PriLow = 0 ; PriLow < operands.length;PriLow++){
			if(operands[PriLow].equals(low.getOperand())){
				break;
			}
		}
		return PriHigh <= PriLow;
	}
	public int EvalExp(GeneticCode g , ListInt WM ,GeneticCode code){
		int left;
		int right;
		if(isLeaf()){
			return getLeaf().parse(g, WM).getSymbol();
		}//and
		if(getOperand().equals("/\\")){
			left = getLeft().EvalExp(g,WM, code  );
			right= getRight().EvalExp(g, WM,code);
			if(left != 0 && right != 0){
				return 1;
			}else{
				return 0;
			}
		}//or
		if(getOperand().equals("\\/")){
			left = getLeft() .EvalExp(g,WM,code );
			right= getRight().EvalExp(g, WM,code);
			if(left != 0 || right != 0){
				return 1;
			}else{
				return 0;
			}
		}//not
		if(getOperand().equals("Y")){
			left = getLeft() .EvalExp(g,WM,code);
			
			if(left != 0){
				return 0;
			}else{
				return 1;
			}
		}//not equals
		if(getOperand().equals("!=")){
			left = getLeft() .EvalExp(g,WM,code);
			right= getRight().EvalExp(g, WM,code);
			if(left !=  0){
				return 1;
			}else{
				return 0;
			}
		}//==
		if(getOperand().equals("==")){
			left = getLeft() .EvalExp(g,WM,code);
			right= getRight().EvalExp(g, WM , code);
			if(left == right ){
				return 1;
			}else{
				return 0;
			}
		}
		if(getOperand().equals(">=")){
			left = getLeft() .EvalExp(g, WM , code);
			right= getRight().EvalExp(g, WM , code);
			if(left >= right ){
				return 1;
			}else{
				return 0;
			}
		}	 
		if(getOperand().equals("<=")){
			left = getLeft() .EvalExp(g, WM , code);
			right= getRight().EvalExp(g, WM , code);
			if(left <= right ){
				return 1;
			}else{
				return 0;
			}
		}
		if(getOperand().equals("<")){
			left = getLeft() .EvalExp(g, WM , code);
			right= getRight().EvalExp(g, WM , code);
			if(left < right ){
				return 1;
			}else{
				return 0;
			}
		}
		if(getOperand().equals("<")){
			left = getLeft() .EvalExp(g, WM , code);
			right= getRight().EvalExp(g, WM , code);
			if(left >= right ){
				return 1;
			}else{
				return 0;
			}
		}
		if(getOperand().equals("Min")){
			left = getLeft() .EvalExp(g, WM , code);
			right= getRight().EvalExp(g, WM , code);
			return Math.min(left, right);
		}
		if(getOperand().equals("Max")){
			left = getLeft() .EvalExp(g, WM , code);
			right= getRight().EvalExp(g, WM , code);
			return (int) Math.max(left, right);
		}
		if(getOperand().equals("+")){
			left = getLeft() .EvalExp(g, WM , code);
			right= getRight().EvalExp(g, WM , code);
			return left + right;
		}
		if(getOperand().equals("-")){
			left = getLeft() .EvalExp(g, WM , code);
			right= getRight().EvalExp(g, WM , code);
			return left = right;
		}
		if(getOperand().equals("*")){
			left = getLeft() .EvalExp(g, WM , code);
			right= getRight().EvalExp(g, WM , code);
			return left * right;
		}
		if(getOperand().equals("/")){
			left = getLeft() .EvalExp(g, WM , code);
			right= getRight().EvalExp(g, WM , code);
			if(right == 0){
				return 0;
			}
			return left / right;
		}
		return -1;
	}
	public static Expression parse(String s){
		return parse(s , new Vector<Expression>());
	}
	//this will build an exSpression which has an operater symbol
	//and an experessio 
	// example     expresssion + expession 
	//the first step is to parse ( expres ) first
	// in order to do this there must be a cursive method to create exp to parse
	
	/** checks if parsed string has a bracket expression
	 * @param s to be parsed to an expresstion
	 */
	private static boolean isNestedExp(String s){
		int leftBrack = 0;
		int riteBrack = 0;
		for(int i =0; i < s.length(); i++){
			//count the number of brackets
			if(s.charAt(i) == '('){
				leftBrack++;
				
			}
			if(s.charAt(i) == ')'){
				riteBrack++;
				
			}
		}
		//is there are equal left and right brackets then there exists
		//nested expresions
		if(leftBrack != 0 && riteBrack != 0 && leftBrack == riteBrack){
			//note if left and right there are not equal
			//there is mismatch bracketing should throw an error
			//TODO but i am lazy
			return true;
		}
		return false;
	}
	//we can now tell is there is a Nested Statment 
	//we need to find where the nested expressions are
	//if we find a breacket create a substring to parse expresstion 
	//place a P saying we parse alone
	/**
	 * @param SearchString , Starting Bracket
	 * @return int location of mathing 
	 */
	private static int  findMatchBracket(String search ,int LeftBracket){
		int leftB = 1;//we give it the start
					//when leftB = zero we found the right bracket
		for(int i = LeftBracket + 1 ; i < search.length(); i++){
			//don't want to count the start
			if(search.charAt(i) == ')'){
				leftB--;
			}
			if(search.charAt(i) == '('){
				leftB++;
			}
			if(leftB == 0){
				return i;
			}
		}
		return -1;
		
	}
	//if there is an nested expression exampl (exp) this will find it and parse it
	//placeing the result into v note that there will be a P# in plave of the expression
	private static String parseNestExpresson(String s , Vector<Expression> v){
		while( s.contains("(")){
			int left =0;//pointers to the left bracket
			int right = 0;//pointer to the right bracket
			for(left = 0; left < s.length(); left++){
				if(s.charAt(left) == '('){
					break;
				}
			}//left char is found
			right = findMatchBracket(s, left);
			v.add(parse(s.substring(left+1, right)));//note substring gets ( , ] range and we don't want to put the ( int
			s = s.substring(0, left) + "P" + (v.size()-1) + s.substring(right +1 );
		}
		return s;
	}
	private static Expression parse(String s, Vector<Expression> nested){
		//base Case if there is nothing to parse return null/error
		if(s.length() == 0){//this will be an error
			return null;
		}
		if(isNestedExp(s)){//there are nested expesstion
			s = parseNestExpresson(s , nested);
		}
		Expression Rval = new Expression();
		for(int i = 0; i < operands.length; i++){
			//specail case not 
			if(s.contains("Y")){
				return parseNot(s,nested);
			}
			if(s.contains(operands[i])){
				//there is somethig to parse
				String[] pice = s.split(operands[i]);
				Rval.Operand = operands[i];
				Rval.left = parse( pice[0],nested );
				Rval.right = parse(s.substring(pice[0].length() + operands[0].length()),nested);
				return Rval;
			}
		}
		//there is now only code  and P exp note not has not been taking care of
		if(s.contains("P")){
			return nested.get( (new Scanner(s)).nextInt());
		}
		Rval.leaf = Code.load(s);
		
		
		return Rval;
	}
	private static Expression parseNot(String s, Vector<Expression> nested) {
		int i;
		for( i = 0; i < s.length(); i++){
			if(s.charAt(i) == 'Y'){
				break;
			}
		}
		Expression RVal = new Expression();
		RVal.Operand = "Y";
		RVal.left = parse(s.substring(i + 1), nested);
		return RVal;
	}
	public static Expression load(String substring) {
		return parse(substring);
		
	}
	

}